package com.meego.common.log;

import java.util.Calendar;
import org.apache.log4j.Layout;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ThrowableInformation;

public class HMTLayout extends Layout {

    private long todms = 0L;
    private char[] buf = new char[1024];
    private long day_offset;
    private long time_expire;

    @Override
    public void activateOptions() {
    }

    @Override
    public String format(LoggingEvent loggingEvent) {
        char[] target = this.buf;
        int len = 0;
        long ms = getTimeOfDay();

        if (ms != this.todms) {
            this.todms = ms;
            len = writeNumber(target, len, (int) (ms / 3600000L % 24L), 2);
            target[(len++)] = ':';
            len = writeNumber(target, len, (int) (ms / 60000L % 60L), 2);
            target[(len++)] = ':';
            len = writeNumber(target, len, (int) (ms / 1000L % 60L), 2);
            target[(len++)] = '.';
        }

        len = 9;
        len = writeNumber(target, len, (int) (ms % 1000L), 3);
        target[(len++)] = ' ';
        Object trans = loggingEvent.getMDC("trans");
        if (trans != null) {
            len = writeString(target, len, trans.toString());
        }
        target[(len++)] = ' ';
        target[(len++)] = '[';
        len = writeString(target, len, loggingEvent.getThreadName());
        target[(len++)] = ']';
        target[(len++)] = ' ';
        String prior = loggingEvent.getLevel().toString();
        int plen = prior.length();
        len = writeString(target, len, prior);
        while (plen < 5) {
            target[(len++)] = ' ';
            ++plen;
        }

        target[(len++)] = ' ';
        len = writeString(target, len, loggingEvent.getLoggerName());
        target[(len++)] = ' ';
        target[(len++)] = '-';
        target[(len++)] = ' ';

        String msg = "";
        Object o = loggingEvent.getMessage();
        if (o != null) {
            msg = msg + o.toString() + '\n';
        }
        ThrowableInformation throwableInfo = loggingEvent.getThrowableInformation();
        if (throwableInfo != null) {
            msg = msg + getNewLineSeparatedString(throwableInfo.getThrowableStrRep());
        }
        if (msg.length() > 0) {
            if (len + msg.length() + 1 > target.length) {
                char[] newbuff = new char[len + msg.length() + 1];
                for (int scan = 0; scan < len; ++scan) {
                    newbuff[scan] = target[scan];
                }

                target = newbuff;
            }
            len = writeString(target, len, msg);
        }
        return new String(target, 0, len);
    }

    private static int writeString(char[] buffer, int ofs, String src) {
        if (src == null) {
            return ofs;
        }
        int src_len = src.length();
        if (src_len == 0) {
            return ofs;
        }
        src.getChars(0, src_len, buffer, ofs);
        return (ofs + src_len);
    }

    private static int writeNumber(char[] buffer, int ofs, int number,
            int len) {
        int scan = len - 1;
        while (scan >= 0) {
            buffer[(ofs + scan)] = (char) (number % 10 + 48);
            number /= 10;
            --scan;
        }
        return (ofs + len);
    }

    private long getTimeOfDay() {
        long now = System.currentTimeMillis();

        synchronized (this) {
            if (now < this.time_expire) {
                return (now - this.day_offset);
            }

        }

        Calendar c = Calendar.getInstance();
        now = c.getTimeInMillis();

        long new_offset = now - (c.get(11) * 3600000) - (c.get(12) * 60000) - (c.get(13) * 1000) - c.get(14);

        c.add(5, 1);
        if (c.getTimeInMillis() - now != 86400000L) {

            c.setTimeInMillis(now);
            c.add(10, 1);
            if (c.getTimeInMillis() - now != 3600000L) {
                c.setTimeInMillis(now);
                c.add(12, 1);
                if (c.getTimeInMillis() - now != 60000L) {
                    c.setTimeInMillis(now);
                    c.add(13, 1);
                    if (c.getTimeInMillis() - now != 1000L) {
                        c.setTimeInMillis(now);
                    }
                }
            }
        }

        long new_expiry = c.getTimeInMillis();
        synchronized (this) {
            this.time_expire = new_expiry;
            this.day_offset = new_offset;
        }

        return (now - this.day_offset);
    }

    private String getNewLineSeparatedString(String[] lines) {
        String newLineSeparatedString = "";
        String nl = System.getProperty("line.separator");
        for (int i = 0; i < lines.length; ++i) {
            newLineSeparatedString = newLineSeparatedString + lines[i] + nl;
        }
        return newLineSeparatedString;
    }

    @Override
    public boolean ignoresThrowable() {
        return false;
    }
}
